package org.custom.spring.boot.mybatis.plugin;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Invocation;
import org.custom.spring.boot.mybatis.entity.MybatisAlgorithmMetadata;
import org.custom.spring.boot.mybatis.utils.MybatisPlugInUtil;
import org.custom.spring.boot.mybatis.utils.MybatisStatementHandlerUtil;

import static cn.hutool.core.util.StrUtil.EMPTY;

/**
 * MybatisPlugin抽象类
 *
 * @author 李坤
 * @date 2022/2/11 11:00
 */
public abstract class AbstractMybatisPlugin implements Interceptor, MybatisPlugin {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 判断是否执行该插件的逻辑
        if (this.determineWhether2Execute(invocation)) {
            String finalSql = EMPTY;
            try {
                // 获取MybatisAlgorithmMetadata-Mybatis插件参数元数据
                MybatisAlgorithmMetadata mybatisAlgorithmMetadata = this.obtainMybatisAlgorithmMetadata(invocation);
                // 根据Mybatis插件参数元数据执行插件算法逻辑
                finalSql = this.obtainMybatisAlgorithm().execute(mybatisAlgorithmMetadata);
            } catch (Exception exception) {
                this.processException(exception, finalSql);
            }
            // 处理代理对象与被处理的SQL之间的关系
            return this.obtainProcess().apply(invocation, finalSql);
        }
        return invocation.proceed();
    }

    /*------------------------------------------------protected methods-----------------------------------------------*/

    /**
     * 处理异常信息与finalSql之间的关系
     *
     * @param exception 异常信息
     * @param finalSql  最终处理的SQL
     */
    protected void processException(Exception exception, String finalSql) {
        exception.printStackTrace();
    }

    /**
     * 获取MybatisAlgorithmMetadata-Mybatis插件参数元数据
     *
     * @param invocation 代理对象
     *
     * @return Mybatis插件参数元数据
     */
    private MybatisAlgorithmMetadata obtainMybatisAlgorithmMetadata(Invocation invocation) {
        String label = null;
        BoundSql boundSql = null;

        Class<?> declaringClass = invocation.getMethod().getDeclaringClass();

        if (Executor.class.isAssignableFrom(declaringClass)) {
            Object[] args = invocation.getArgs();
            MappedStatement statement = (MappedStatement) args[0];

            label = statement.getId();
            boundSql = statement.getBoundSql(args[1]);
        } else if (StatementHandler.class.isAssignableFrom(declaringClass)) {
            // 获取被代理对象-StatementHandler
            StatementHandler handler = (StatementHandler) invocation.getTarget();

            // 绑定的 SQL 数据信息
            boundSql = handler.getBoundSql();
            // 获取给定声明处理器中的SQL-label
            label = MybatisStatementHandlerUtil.getBoundSqlLabel(handler);
        }
        // 解析参数映射，并将其转换为 MybatisMapping 对象
        return new MybatisAlgorithmMetadata(label, boundSql, MybatisPlugInUtil.parseParameterMapping(boundSql));
    }
}
